package server.maps;

import constants.BattleConstants;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.io.File;
import constants.GameConstants;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.MaplePortal;
import server.life.AbstractLoadedMapleLife;
import server.life.MapleLifeFactory;
import server.life.MapleMonster;
import server.life.MapleNPC;
import server.maps.MapleNodes.DirectionInfo;
import server.maps.MapleNodes.MapleNodeInfo;
import server.maps.MapleNodes.MaplePlatform;
import tools.Pair;
import tools.StringUtil;

public class MapleMapFactory {

    private final MapleDataProvider source = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("net.sf.odinms.wzpath") + "/Map.wz"));
    private final MapleData nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("net.sf.odinms.wzpath") + "/String.wz")).getData("Map.img");
    private final HashMap<Integer, MapleMap> maps = new HashMap<>();
    private final HashMap<Integer, MapleMap> instanceMap = new HashMap<>();
    private final Map<Integer, Integer> DeStorymaps = new HashMap<>();
    private final ReentrantLock lock = new ReentrantLock();
    private int channel;

    public MapleMapFactory(int channel) {
        this.channel = channel;
    }

    public final MapleMap getMap(final int mapid) {
        return getMap(mapid, true, true, true);
    }

    //backwards-compatible
    public final MapleMap getMap(final int mapid, final boolean respawns, final boolean npcs) {
        return getMap(mapid, respawns, npcs, true);
    }

    public final MapleMap getMap(final int mapid, final boolean respawns, final boolean npcs, final boolean reactors) {
        Integer omapid = Integer.valueOf(mapid);
        MapleMap map = maps.get(omapid);
        if (map == null) {
            lock.lock();
            try {
                if (DeStorymaps.get(omapid) != null) {
                    return null;
                }
                map = maps.get(omapid);
                if (map != null) {
                    return map;
                }
                MapleData mapData = null;
                try {
                    mapData = source.getData(getMapName(mapid));
                } catch (Exception e) {
                    return null;
                }
                if (mapData == null) {
                    return null;
                }
                MapleData link = mapData.getChildByPath("info/link");
                if (link != null) {
                    mapData = source.getData(getMapName(MapleDataTool.getIntConvert("info/link", mapData)));
                }

                float monsterRate = 0;
                if (respawns) {
                    MapleData mobRate = mapData.getChildByPath("info/mobRate");
                    if (mobRate != null) {
                        monsterRate = ((Float) mobRate.getData()).floatValue();
                    }
                }
                map = new MapleMap(mapid, channel, MapleDataTool.getInt("info/returnMap", mapData), monsterRate);

                loadPortals(map, mapData.getChildByPath("portal"));
                map.setTop(MapleDataTool.getInt(mapData.getChildByPath("info/VRTop"), 0));
                map.setLeft(MapleDataTool.getInt(mapData.getChildByPath("info/VRLeft"), 0));
                map.setBottom(MapleDataTool.getInt(mapData.getChildByPath("info/VRBottom"), 0));
                map.setRight(MapleDataTool.getInt(mapData.getChildByPath("info/VRRight"), 0));
                List<MapleFoothold> allFootholds = new LinkedList<>();
                Point lBound = new Point();
                Point uBound = new Point();
                MapleFoothold fh;

                for (MapleData footRoot : mapData.getChildByPath("foothold")) {
                    for (MapleData footCat : footRoot) {
                        for (MapleData footHold : footCat) {
                            fh = new MapleFoothold(new Point(
                                    MapleDataTool.getInt(footHold.getChildByPath("x1"), 0), MapleDataTool.getInt(footHold.getChildByPath("y1"), 0)), new Point(
                                            MapleDataTool.getInt(footHold.getChildByPath("x2"), 0), MapleDataTool.getInt(footHold.getChildByPath("y2"), 0)), Integer.parseInt(footHold.getName()));
                            fh.setPrev((short) MapleDataTool.getInt(footHold.getChildByPath("prev"), 0));
                            fh.setNext((short) MapleDataTool.getInt(footHold.getChildByPath("next"), 0));

                            if (fh.getX1() < lBound.x) {
                                lBound.x = fh.getX1();
                            }
                            if (fh.getX2() > uBound.x) {
                                uBound.x = fh.getX2();
                            }
                            if (fh.getY1() < lBound.y) {
                                lBound.y = fh.getY1();
                            }
                            if (fh.getY2() > uBound.y) {
                                uBound.y = fh.getY2();
                            }
                            allFootholds.add(fh);
                        }
                    }
                }
                MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
                for (MapleFoothold foothold : allFootholds) {
                    fTree.insert(foothold);
                }
                map.setFootholds(fTree);
                if (map.getTop() == 0) {
                    map.setTop(lBound.y);
                }
                if (map.getBottom() == 0) {
                    map.setBottom(uBound.y);
                }
                if (map.getLeft() == 0) {
                    map.setLeft(lBound.x);
                }
                if (map.getRight() == 0) {
                    map.setRight(uBound.x);
                }
                int bossid = -1;
                String msg = null;
                if (mapData.getChildByPath("info/timeMob") != null) {
                    bossid = MapleDataTool.getInt(mapData.getChildByPath("info/timeMob/id"), 0);
                    msg = MapleDataTool.getString(mapData.getChildByPath("info/timeMob/message"), null);
                }

                // load life data (npc, monsters)
                List<Point> herbRocks = new ArrayList<>();
                int lowestLevel = 200, highestLevel = 0;
                String type, limited;
                AbstractLoadedMapleLife myLife;

                for (MapleData life : mapData.getChildByPath("life")) {
                    type = MapleDataTool.getString(life.getChildByPath("type"));
                    limited = MapleDataTool.getString("limitedname", life, "");
                    if ((npcs || !type.equals("n")) && !limited.equals("Stage0")) { //alien pq stuff
                        myLife = loadLife(life, MapleDataTool.getString(life.getChildByPath("id")), type);

                        if (myLife instanceof MapleMonster /*&& !BattleConstants.isBattleMap(mapid) && !GameConstants.isNoSpawn(mapid)*/) {
                            final MapleMonster mob = (MapleMonster) myLife;

                            herbRocks.add(map.addMonsterSpawn(mob,
                                    MapleDataTool.getInt("mobTime", life, 0),
                                    (byte) MapleDataTool.getInt("team", life, -1),
                                    mob.getId() == bossid ? msg : null).getPosition());
                            if (mob.getStats().getLevel() > highestLevel && !mob.getStats().isBoss()) {
                                highestLevel = mob.getStats().getLevel();
                            }
                            if (mob.getStats().getLevel() < lowestLevel && !mob.getStats().isBoss()) {
                                lowestLevel = mob.getStats().getLevel();
                            }
                        } else if (myLife instanceof MapleNPC) {
                            map.addMapObject(myLife);
                        }
                    }
                }
                addAreaBossSpawn(map);
                map.setCreateMobInterval((short) MapleDataTool.getInt(mapData.getChildByPath("info/createMobInterval"), 9000));
                map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
                map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
                map.loadMonsterRate(true);
                map.setNodes(loadNodes(mapid, mapData));

                //load reactor data
                String id;
                if (reactors && mapData.getChildByPath("reactor") != null /*&& !BattleConstants.isBattleMap(mapid)*/) {
                    for (MapleData reactor : mapData.getChildByPath("reactor")) {
                        id = MapleDataTool.getString(reactor.getChildByPath("id"));
                        if (id != null) {
                            map.spawnReactor(loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0)));
                        }
                    }
                }
                map.setFirstUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onFirstUserEnter"), ""));
                map.setUserEnter(mapid == GameConstants.JAIL ? "jail" : MapleDataTool.getString(mapData.getChildByPath("info/onUserEnter"), ""));
                // 20160728
//                if (reactors && herbRocks.size() > 0 && highestLevel >= 30 && map.getFirstUserEnter().equals("") && map.getUserEnter().equals("")) {
//                    final List<Integer> allowedSpawn = new ArrayList<Integer>(24);
//                    allowedSpawn.add(100011);
//                    allowedSpawn.add(200011);
//                    if (highestLevel >= 100) {
//                        for (int i = 0; i < 10; i++) {
//                            for (int x = 0; x < 4; x++) { //to make heartstones rare
//                                allowedSpawn.add(100000 + i);
//                                allowedSpawn.add(200000 + i);
//                            }
//                        }
//                    } else {
//                        for (int i = (lowestLevel % 10 > highestLevel % 10 ? 0 : (lowestLevel % 10)); i < (highestLevel % 10); i++) {
//                            for (int x = 0; x < 4; x++) { //to make heartstones rare
//                                allowedSpawn.add(100000 + i);
//                                allowedSpawn.add(200000 + i);
//                            }
//                        }
//                    }
//                    final int numSpawn = Randomizer.nextInt(allowedSpawn.size()) / 6; //0-7
//                    for (int i = 0; i < numSpawn && !herbRocks.isEmpty(); i++) {
//                        final int idd = allowedSpawn.get(Randomizer.nextInt(allowedSpawn.size()));
//                        final int theSpawn = Randomizer.nextInt(herbRocks.size());
//                        final MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(idd), idd);
//                        myReactor.setPosition(herbRocks.get(theSpawn));
//                        myReactor.setDelay(idd % 100 == 11 ? 60000 : 5000); //in the reactor's wz
//                        map.spawnReactor(myReactor);
//                        herbRocks.remove(theSpawn);
//                    }
//                }

                try {
                    map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(omapid)), ""));
                    map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(omapid)), ""));
                } catch (Exception e) {
                    map.setMapName("");
                    map.setStreetName("");
                }
                map.setClock(mapData.getChildByPath("clock") != null); //clock was changed in wz to have x,y,width,height
                map.setEverlast(MapleDataTool.getInt(mapData.getChildByPath("info/everlast"), 0) > 0);
                map.setTown(MapleDataTool.getInt(mapData.getChildByPath("info/town"), 0) > 0);
                map.setSoaring(MapleDataTool.getInt(mapData.getChildByPath("info/needSkillForFly"), 0) > 0);
                map.setPersonalShop(MapleDataTool.getInt(mapData.getChildByPath("info/personalShop"), 0) > 0);
                map.setForceMove(MapleDataTool.getInt(mapData.getChildByPath("info/lvForceMove"), 0));
                map.setHPDec(MapleDataTool.getInt(mapData.getChildByPath("info/decHP"), 0));
                map.setHPDecInterval(MapleDataTool.getInt(mapData.getChildByPath("info/decHPInterval"), 10000));
                map.setHPDecProtect(MapleDataTool.getInt(mapData.getChildByPath("info/protectItem"), 0));
                map.setForcedReturnMap(mapid == 0 ? 999999999 : MapleDataTool.getInt(mapData.getChildByPath("info/forcedReturn"), 999999999));
                map.setTimeLimit(MapleDataTool.getInt(mapData.getChildByPath("info/timeLimit"), -1));
                map.setFieldLimit(MapleDataTool.getInt(mapData.getChildByPath("info/fieldLimit"), 0));
                map.setRecoveryRate(MapleDataTool.getFloat(mapData.getChildByPath("info/recovery"), 1));
                map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
                map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
                map.setConsumeItemCoolTime(MapleDataTool.getInt(mapData.getChildByPath("info/consumeItemCoolTime"), 0));

                maps.put(omapid, map);
            } finally {
                lock.unlock();
            }
        }
        return map;
    }

    public MapleMap getInstanceMap(final int instanceid) {
        return instanceMap.get(instanceid);
    }

    public void removeInstanceMap(final int instanceid) {
        lock.lock();
        try {
            if (isInstanceMapLoaded(instanceid)) {
                getInstanceMap(instanceid).checkStates("");
                instanceMap.remove(instanceid);
            }
        } finally {
            lock.unlock();
        }
    }

    public void removeMap(final int instanceid) {
        lock.lock();
        try {
            if (isMapLoaded(instanceid)) {
                getMap(instanceid).checkStates("");
                maps.remove(instanceid);
            }
        } finally {
            lock.unlock();
        }
    }

    public MapleMap CreateInstanceMap(int mapid, boolean respawns, boolean npcs, boolean reactors, int instanceid) {
        lock.lock();
        try {
            if (isInstanceMapLoaded(instanceid)) {
                return getInstanceMap(instanceid);
            }
        } finally {
            lock.unlock();
        }
        MapleData mapData = null;
        try {
            mapData = source.getData(getMapName(mapid));
        } catch (Exception e) {
            return null;
        }
        if (mapData == null) {
            return null;
        }
        MapleData link = mapData.getChildByPath("info/link");
        if (link != null) {
            mapData = source.getData(getMapName(MapleDataTool.getIntConvert("info/link", mapData)));
        }

        float monsterRate = 0;
        if (respawns) {
            MapleData mobRate = mapData.getChildByPath("info/mobRate");
            if (mobRate != null) {
                monsterRate = ((Float) mobRate.getData()).floatValue();
            }
        }
        MapleMap map = new MapleMap(mapid, channel, MapleDataTool.getInt("info/returnMap", mapData), monsterRate);
        loadPortals(map, mapData.getChildByPath("portal"));
        map.setTop(MapleDataTool.getInt(mapData.getChildByPath("info/VRTop"), 0));
        map.setLeft(MapleDataTool.getInt(mapData.getChildByPath("info/VRLeft"), 0));
        map.setBottom(MapleDataTool.getInt(mapData.getChildByPath("info/VRBottom"), 0));
        map.setRight(MapleDataTool.getInt(mapData.getChildByPath("info/VRRight"), 0));
        List<MapleFoothold> allFootholds = new LinkedList<>();
        Point lBound = new Point();
        Point uBound = new Point();
        for (MapleData footRoot : mapData.getChildByPath("foothold")) {
            for (MapleData footCat : footRoot) {
                for (MapleData footHold : footCat) {
                    MapleFoothold fh = new MapleFoothold(new Point(
                            MapleDataTool.getInt(footHold.getChildByPath("x1")), MapleDataTool.getInt(footHold.getChildByPath("y1"))), new Point(
                                    MapleDataTool.getInt(footHold.getChildByPath("x2")), MapleDataTool.getInt(footHold.getChildByPath("y2"))), Integer.parseInt(footHold.getName()));
                    fh.setPrev((short) MapleDataTool.getInt(footHold.getChildByPath("prev")));
                    fh.setNext((short) MapleDataTool.getInt(footHold.getChildByPath("next")));

                    if (fh.getX1() < lBound.x) {
                        lBound.x = fh.getX1();
                    }
                    if (fh.getX2() > uBound.x) {
                        uBound.x = fh.getX2();
                    }
                    if (fh.getY1() < lBound.y) {
                        lBound.y = fh.getY1();
                    }
                    if (fh.getY2() > uBound.y) {
                        uBound.y = fh.getY2();
                    }
                    allFootholds.add(fh);
                }
            }
        }
        MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
        for (MapleFoothold fh : allFootholds) {
            fTree.insert(fh);
        }
        map.setFootholds(fTree);
        if (map.getTop() == 0) {
            map.setTop(lBound.y);
        }
        if (map.getBottom() == 0) {
            map.setBottom(uBound.y);
        }
        if (map.getLeft() == 0) {
            map.setLeft(lBound.x);
        }
        if (map.getRight() == 0) {
            map.setRight(uBound.x);
        }
        int bossid = -1;
        String msg = null;
        if (mapData.getChildByPath("info/timeMob") != null) {
            bossid = MapleDataTool.getInt(mapData.getChildByPath("info/timeMob/id"), 0);
            msg = MapleDataTool.getString(mapData.getChildByPath("info/timeMob/message"), null);
        }

        // load life data (npc, monsters)
        String type, limited;
        AbstractLoadedMapleLife myLife;

        for (MapleData life : mapData.getChildByPath("life")) {
            type = MapleDataTool.getString(life.getChildByPath("type"));
            limited = MapleDataTool.getString("limitedname", life, "");
            if ((npcs || !type.equals("n")) && limited.equals("")) {
                myLife = loadLife(life, MapleDataTool.getString(life.getChildByPath("id")), type);

                if (myLife instanceof MapleMonster /*&& !BattleConstants.isBattleMap(mapid) && !GameConstants.isNoSpawn(mapid)*/) {
                    final MapleMonster mob = (MapleMonster) myLife;

                    map.addMonsterSpawn(mob,
                            MapleDataTool.getInt("mobTime", life, 0),
                            (byte) MapleDataTool.getInt("team", life, -1),
                            mob.getId() == bossid ? msg : null);

                } else if (myLife instanceof MapleNPC) {
                    map.addMapObject(myLife);
                }
            }
        }
        addAreaBossSpawn(map);
        map.setCreateMobInterval((short) MapleDataTool.getInt(mapData.getChildByPath("info/createMobInterval"), 9000));
        map.setFixedMob(MapleDataTool.getInt(mapData.getChildByPath("info/fixedMobCapacity"), 0));
        map.setPartyBonusRate(GameConstants.getPartyPlay(mapid, MapleDataTool.getInt(mapData.getChildByPath("info/partyBonusR"), 0)));
        map.loadMonsterRate(true);
        map.setNodes(loadNodes(mapid, mapData));

        //load reactor data
        String id;
        if (reactors && mapData.getChildByPath("reactor") != null /*&& !BattleConstants.isBattleMap(mapid)*/) {
            for (MapleData reactor : mapData.getChildByPath("reactor")) {
                id = MapleDataTool.getString(reactor.getChildByPath("id"));
                if (id != null) {
                    map.spawnReactor(loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0)));
                }
            }
        }
        try {
            map.setMapName(MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), ""));
            map.setStreetName(MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), ""));
        } catch (Exception e) {
            map.setMapName("");
            map.setStreetName("");
        }
        map.setClock(MapleDataTool.getInt(mapData.getChildByPath("info/clock"), 0) > 0);
        map.setEverlast(MapleDataTool.getInt(mapData.getChildByPath("info/everlast"), 0) > 0);
        map.setTown(MapleDataTool.getInt(mapData.getChildByPath("info/town"), 0) > 0);
        map.setSoaring(MapleDataTool.getInt(mapData.getChildByPath("info/needSkillForFly"), 0) > 0);
        map.setForceMove(MapleDataTool.getInt(mapData.getChildByPath("info/lvForceMove"), 0));
        map.setHPDec(MapleDataTool.getInt(mapData.getChildByPath("info/decHP"), 0));
        map.setHPDecInterval(MapleDataTool.getInt(mapData.getChildByPath("info/decHPInterval"), 10000));
        map.setHPDecProtect(MapleDataTool.getInt(mapData.getChildByPath("info/protectItem"), 0));
        map.setForcedReturnMap(MapleDataTool.getInt(mapData.getChildByPath("info/forcedReturn"), 999999999));
        map.setTimeLimit(MapleDataTool.getInt(mapData.getChildByPath("info/timeLimit"), -1));
        map.setFieldLimit(MapleDataTool.getInt(mapData.getChildByPath("info/fieldLimit"), 0));
        map.setFirstUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onFirstUserEnter"), ""));
        map.setUserEnter(MapleDataTool.getString(mapData.getChildByPath("info/onUserEnter"), ""));
        map.setRecoveryRate(MapleDataTool.getFloat(mapData.getChildByPath("info/recovery"), 1));
        map.setConsumeItemCoolTime(MapleDataTool.getInt(mapData.getChildByPath("info/consumeItemCoolTime"), 0));
        map.setInstanceId(instanceid);
        lock.lock();
        try {
            instanceMap.put(instanceid, map);
        } finally {
            lock.unlock();
        }
        return map;
    }

    public int getLoadedMaps() {
        return maps.size();
    }

    public boolean isMapLoaded(int mapId) {
        return maps.containsKey(mapId);
    }

    public boolean isInstanceMapLoaded(int instanceid) {
        return instanceMap.containsKey(instanceid);
    }

    public void clearLoadedMap() {
        lock.lock();
        try {
            maps.clear();
        } finally {
            lock.unlock();
        }
    }

    public List<MapleMap> getAllLoadedMaps() {
        List<MapleMap> ret = new ArrayList<>();
        lock.lock();
        try {
            ret.addAll(maps.values());
            ret.addAll(instanceMap.values());
        } finally {
            lock.unlock();
        }
        return ret;
    }

    public Collection<MapleMap> getAllMaps() {
        return maps.values();
    }

    private AbstractLoadedMapleLife loadLife(MapleData life, String id, String type) {
        AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(Integer.parseInt(id), type);
        if (myLife == null) {
            return null;
        }
        myLife.setCy(MapleDataTool.getInt(life.getChildByPath("cy")));
        MapleData dF = life.getChildByPath("f");
        if (dF != null) {
            myLife.setF(MapleDataTool.getInt(dF));
        }
        myLife.setFh(MapleDataTool.getInt(life.getChildByPath("fh")));
        myLife.setRx0(MapleDataTool.getInt(life.getChildByPath("rx0")));
        myLife.setRx1(MapleDataTool.getInt(life.getChildByPath("rx1")));
        myLife.setPosition(new Point(MapleDataTool.getInt(life.getChildByPath("x")), MapleDataTool.getInt(life.getChildByPath("y"))));

        if (MapleDataTool.getInt("hide", life, 0) == 1 && myLife instanceof MapleNPC) {
            myLife.setHide(true);
//		} else if (hide > 1) {
//			System.err.println("Hide > 1 ("+ hide +")");
        }
        return myLife;
    }

    private final MapleReactor loadReactor(final MapleData reactor, final String id, final byte FacingDirection) {
        final MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(Integer.parseInt(id)), Integer.parseInt(id));

        myReactor.setFacingDirection(FacingDirection);
        myReactor.setPosition(new Point(MapleDataTool.getInt(reactor.getChildByPath("x")), MapleDataTool.getInt(reactor.getChildByPath("y"))));
        myReactor.setDelay(MapleDataTool.getInt(reactor.getChildByPath("reactorTime")) * 1000);
        myReactor.setName(MapleDataTool.getString(reactor.getChildByPath("name"), ""));

        return myReactor;
    }

    private String getMapName(int mapid) {
        String mapName = StringUtil.getLeftPaddedStr(Integer.toString(mapid), '0', 9);
        StringBuilder builder = new StringBuilder("Map/Map");
        builder.append(mapid / 100000000);
        builder.append("/");
        builder.append(mapName);
        builder.append(".img");

        mapName = builder.toString();
        return mapName;
    }

    private String getMapStringName(int mapid) {
        StringBuilder builder = new StringBuilder();
        if (mapid < 100000000) {
            builder.append("maple");
        } else if ((mapid >= 100000000 && mapid < 200000000) || mapid / 100000 == 5540) {
            builder.append("victoria");
        } else if (mapid >= 200000000 && mapid < 300000000) {
            builder.append("ossyria");
        } else if (mapid >= 300000000 && mapid < 400000000) {
            builder.append("3rd");
        } else if (mapid >= 500000000 && mapid < 510000000) {
            builder.append(GameConstants.GMS ? "thai" : "TH");
        } else if (mapid >= 555000000 && mapid < 556000000) {
            builder.append("SG");
        } else if (mapid >= 540000000 && mapid < 600000000) {
            builder.append(GameConstants.GMS ? "singapore" : "SG");
        } else if (mapid >= 682000000 && mapid < 683000000) {
            builder.append(GameConstants.GMS ? "HalloweenGL" : "GL");
        } else if (mapid >= 600000000 && mapid < 670000000) {
            builder.append(GameConstants.GMS ? "MasteriaGL" : "GL");
        } else if (mapid >= 677000000 && mapid < 678000000) {
            builder.append(GameConstants.GMS ? "Episode1GL" : "GL");
        } else if (mapid >= 670000000 && mapid < 682000000) {
            builder.append(GameConstants.GMS ? "weddingGL" : "GL");
        } else if (mapid >= 687000000 && mapid < 688000000) {
            builder.append("Gacha_GL");
        } else if (mapid >= 689000000 && mapid < 690000000) {
            builder.append("CTF_GL");
        } else if (mapid >= 683000000 && mapid < 684000000) {
            builder.append("event");
        } else if (mapid >= 684000000 && mapid < 685000000) {
            builder.append("event_5th");
        } else if (mapid >= 700000000 && mapid < 700000300) {
            builder.append("wedding");
        } else if (mapid >= 701000000 && mapid < 701020000) {
            builder.append("china");
        } else if ((mapid >= 702090000 && mapid <= 702100000) || (mapid >= 740000000 && mapid < 741000000)) {
            builder.append("TW");
        } else if (mapid >= 702000000 && mapid < 742000000) {
            builder.append("CN");
        } else if (mapid >= 800000000 && mapid < 900000000) {
            builder.append(GameConstants.GMS ? "jp" : "JP");
        } else {
            builder.append("etc");
        }
        builder.append("/");
        builder.append(mapid);

        return builder.toString();
    }

    public void setChannel(int channel) {
        this.channel = channel;
    }

    private void addAreaBossSpawn(final MapleMap map) {
        int monsterid = -1;
        int mobtime = -1;
        String msg = null;
        boolean shouldSpawn = true;
        Point pos1 = null, pos2 = null, pos3 = null;

        switch (map.getId()) {
//            case 104010200: // Mano
//                mobtime = 1200;
//                monsterid = 2220000;
//                msg = "A cool breeze was felt when Mano appeared.";
//                pos1 = new Point(189, 2);
//                pos2 = new Point(478, 250);
//                pos3 = new Point(611, 489);
//                break;
//            case 102020500: // Stumpy
//                mobtime = 1200;
//                monsterid = 3220000;
//                msg = "Stumpy has appeared with a stumping sound that rings the Stone Mountain.";
//                pos1 = new Point(1121, 2130);
//                pos2 = new Point(483, 2171);
//                pos3 = new Point(1474, 1706);
//                break;
//            case 100020101: //Mushmom
//                mobtime = 1200;
//                monsterid = 6130101;
//                msg = "Mushmom has appeared.";
//                pos1 = new Point(-311, 201);
//                pos2 = new Point(-903, 197);
//                pos3 = new Point(-568, 196);
//                break;
//            case 100020301: //Blue Mushmom
//                mobtime = 1200;
//                monsterid = 8220007;
//                msg = "Blue Mushmom has appeared.";
//                pos1 = new Point(-188, -657);
//                pos2 = new Point(625, -660);
//                pos3 = new Point(508, -648);
//                break;
//            case 100020401: //Zombie Mushmom
//                mobtime = 1200;
//                monsterid = 6300005;
//                msg = "Zombie Mushmom has appeared.";
//                pos1 = new Point(-130, -773);
//                pos2 = new Point(504, -760);
//                pos3 = new Point(608, -641);
//                break;
//            case 120030500: // King Clang
//                mobtime = 1200;
//                monsterid = 5220001;
//                msg = "A strange turban shell has appeared on the beach.";
//                pos1 = new Point(-355, 179);
//                pos2 = new Point(-1283, -113);
//                pos3 = new Point(-571, -593);
//                break;
//            case 250010304: // Tae Roon
//                mobtime = 2100;
//                monsterid = 7220000;
//                msg = "Tae Roon appeared with a loud growl.";
//                pos1 = new Point(-210, 33);
//                pos2 = new Point(-234, 393);
//                pos3 = new Point(-654, 33);
//                break;
//            case 200010300: // Eliza
//                mobtime = 1200;
//                monsterid = 8220000;
//                msg = "Eliza has appeared with a black whirlwind.";
//                pos1 = new Point(665, 83);
//                pos2 = new Point(672, -217);
//                pos3 = new Point(-123, -217);
//                break;
//            case 250010503: // Ghost Priest
//                mobtime = 1800;
//                monsterid = 7220002;
//                msg = "The area fills with an unpleasant force of evil.. even the occasional ones of the cats sound disturbing";
//                pos1 = new Point(-303, 543);
//                pos2 = new Point(227, 543);
//                pos3 = new Point(719, 543);
//                break;
//            case 222010310: // Old Fox
//                mobtime = 2700;
//                monsterid = 7220001;
//                msg = "As the moon light dims,a long fox cry can be heard and the presence of the old fox can be felt.";
//                pos1 = new Point(-169, -147);
//                pos2 = new Point(-517, 93);
//                pos3 = new Point(247, 93);
//                break;
//            case 103030400: // Dale
//                mobtime = 1800;
//                monsterid = 6220000;
//                msg = "The huge crocodile Dyle has come out from the swamp.";
//                pos1 = new Point(-831, 109);
//                pos2 = new Point(1525, -75);
//                pos3 = new Point(-511, 107);
//                break;
//            case 101040300: // Faust
//                mobtime = 1800;
//                monsterid = 5220002;
//                msg = "The blue fog became darker when Faust appeared.";
//                pos1 = new Point(600, -600);
//                pos2 = new Point(600, -800);
//                pos3 = new Point(600, -300);
//                break;
//            case 220050100: // Timer
//                mobtime = 1500;
//                monsterid = 5220003;
//                msg = "Click clock! Timer has appeared with an irregular clock sound.";
//                pos1 = new Point(-467, 1032);
//                pos2 = new Point(532, 1032);
//                pos3 = new Point(-47, 1032);
//                break;
//            case 221040301: // Zeno
//                mobtime = 2400;
//                monsterid = 6220001;
//                msg = "Zeno has appeared with a heavy sound of machinery.";
//                pos1 = new Point(-4134, 416);
//                pos2 = new Point(-4283, 776);
//                pos3 = new Point(-3292, 776);
//                break;
//            case 240040401: // Lev
//                mobtime = 7200;
//                monsterid = 8220003;
//                msg = "Leviathan has appeared with a cold wind from over the gorge.";
//                pos1 = new Point(-15, 2481);
//                pos2 = new Point(127, 1634);
//                pos3 = new Point(159, 1142);
//                break;
//            case 260010201: // Deo
//                mobtime = 3600;
//                monsterid = 3220001;
//                msg = "Deo slowly appeared out of the sand dust.";
//                pos1 = new Point(-215, 275);
//                pos2 = new Point(298, 275);
//                pos3 = new Point(592, 275);
//                break;
//            case 251010102: // Centipede
//                mobtime = 3600;
//                monsterid = 5220004;
//                msg = "A giant centipede appeared.";
//                pos1 = new Point(-41, 124);
//                pos2 = new Point(-173, 126);
//                pos3 = new Point(79, 118);
//                break;
//            case 261030000: // Chimera
//                mobtime = 2700;
//                monsterid = 8220002;
//                msg = "Chimera has appeared out of the darkness of the underground with a glitter in her eyes.";
//                pos1 = new Point(-1094, -405);
//                pos2 = new Point(-772, -116);
//                pos3 = new Point(-108, 181);
//                break;
//            case 230020100: // Sherp
//                mobtime = 2700;
//                monsterid = 4220000;
//                msg = "A strange shell has appeared from a grove of seaweed.";
//                pos1 = new Point(-291, -20);
//                pos2 = new Point(-272, -500);
//                pos3 = new Point(-462, 640);
//                break;
//            case 103020320: // Shade
//                mobtime = 1800;
//                monsterid = 5090000;
//                msg = "Shade has appeared.";
//                pos1 = new Point(79, 174);
//                pos2 = new Point(-223, 296);
//                pos3 = new Point(80, 275);
//                break;
//            case 103020420: // Shade
//                mobtime = 1800;
//                monsterid = 5090000;
//                msg = "Shade has appeared.";
//                pos1 = new Point(2241, 301);
//                pos2 = new Point(1990, 301);
//                pos3 = new Point(1684, 307);
//                break;
//            case 261020300: // Camera
//                mobtime = 2700;
//                monsterid = 7090000;
//                msg = "A camera has appeared.";
//                pos1 = new Point(312, 157);
//                pos2 = new Point(539, 136);
//                pos3 = new Point(760, 141);
//                break;
//            case 261020401: // Deet and Roi
//                mobtime = 2700;
//                monsterid = 8090000;
//                msg = "Deet and Roi has appeared.";
//                pos1 = new Point(-263, 155);
//                pos2 = new Point(-436, 122);
//                pos3 = new Point(22, 144);
//                break;
//            case 250020300: // Master Dummy
//                mobtime = 2700;
//                monsterid = 5090001;
//                msg = "Master Dummy has appeared.";
//                pos1 = new Point(1208, 27);
//                pos2 = new Point(1654, 40);
//                pos3 = new Point(927, -502);
//                break;
//            case 211050000: // Snow Witch
//                mobtime = 2700;
//                monsterid = 6090001;
//                msg = "The witch of snow has appeared.";
//                pos1 = new Point(-233, -431);
//                pos2 = new Point(-370, -426);
//                pos3 = new Point(-526, -420);
//                break;
//            case 261010003: // Rurumo
//                mobtime = 2700;
//                monsterid = 6090004;
//                msg = "Rurumo has appeared.";
//                pos1 = new Point(-861, 301);
//                pos2 = new Point(-703, 301);
//                pos3 = new Point(-426, 287);
//                break;
//            case 222010300: // Scholar Ghost
//                mobtime = 2700;
//                monsterid = 6090003;
//                msg = "A wise ghost has appeared.";
//                pos1 = new Point(1300, -400);
//                pos2 = new Point(1100, -100);
//                pos3 = new Point(1100, 100);
//                break;
//            case 251010101: // Bamboo Warrior
//                mobtime = 2700;
//                monsterid = 6090002;
//                msg = "A warrior with bamboo has appeared.";
//                pos1 = new Point(-15, -449);
//                pos2 = new Point(-114, -442);
//                pos3 = new Point(-255, -446);
//                break;
//            case 211041400: // Riche
//                mobtime = 2700;
//                monsterid = 6090000;
//                msg = "Riche has appeared.";
//                pos1 = new Point(1672, 82);
//                pos2 = new Point(2071, 10);
//                pos3 = new Point(1417, 57);
//                break;
//            case 105030500: // Rog
//                mobtime = 2700;
//                monsterid = 8130100;
//                msg = "Jr. Balrog has appeared.";
//                pos1 = new Point(1275, -399);
//                pos2 = new Point(1254, -412);
//                pos3 = new Point(1058, -427);
//                break;
//            case 105020400: // Snack Bar
//                mobtime = 2700;
//                monsterid = 8220008;
//                msg = "A mysterious shop appeared.";
//                pos1 = new Point(-163, 82);
//                pos2 = new Point(958, 107);
//                pos3 = new Point(706, -206);
//                break;
//            case 211040101: // Snowman
//                mobtime = 3600;
//                monsterid = 8220001;
//                msg = "A snowman covered in ice has appeared.";
//                pos1 = new Point(485, 244);
//                pos2 = new Point(-60, 249);
//                pos3 = new Point(208, 255);
//                break;
//            case 910000000: // FM
//                if (channel == 7) {
//                    mobtime = 3600;
//                    monsterid = 9420015;
//                    msg = "NooNoo has appeared out of anger.";
//                    pos1 = new Point(498, 4);
//                    pos2 = new Point(498, 4);
//                    pos3 = new Point(498, 4);
//                } else if (channel == 8) {
//                    mobtime = 3600;
//                    monsterid = 9400700;
//                    msg = "Giant Tomato has appeared.";
//                    pos1 = new Point(498, 4);
//                    pos2 = new Point(498, 4);
//                    pos3 = new Point(498, 4);
//                } else if (channel == 9) {
//                    mobtime = 3600;
//                    monsterid = 9400734;
//                    msg = "Giant Tomato has appeared.";
//                    pos1 = new Point(498, 4);
//                    pos2 = new Point(498, 4);
//                    pos3 = new Point(498, 4);
//                }
//                break;
//            case 209000000: // Happyvile
//                mobtime = 300;
//                monsterid = 9500317;
//                msg = "Little Snowman has appeared!";
//                pos1 = new Point(-115, 154);
//                pos2 = new Point(-115, 154);
//                pos3 = new Point(-115, 154);
//                break;
//            case 677000001:
//                mobtime = 60;
//                monsterid = 9400612;
//                msg = "Marbas has appeared.";
//                pos1 = new Point(99, 60);
//                pos2 = new Point(99, 60);
//                pos3 = new Point(99, 60);
//                break;
//            case 677000003:
//                mobtime = 60;
//                monsterid = 9400610;
//                msg = "Amdusias has appeared.";
//                pos1 = new Point(6, 35);
//                pos2 = new Point(6, 35);
//                pos3 = new Point(6, 35);
//                break;
//            case 677000005:
//                mobtime = 60;
//                monsterid = 9400609;
//                msg = "Andras has appeared.";
//                pos1 = new Point(-277, 78); //on the spawnpoint
//                pos2 = new Point(547, 86); //bottom of right ladder
//                pos3 = new Point(-347, 80); //bottom of left ladder
//                break;
//            case 677000007:
//                mobtime = 60;
//                monsterid = 9400611;
//                msg = "Crocell has appeared.";
//                pos1 = new Point(117, 73);
//                pos2 = new Point(117, 73);
//                pos3 = new Point(117, 73);
//                break;
//            case 677000009:
//                mobtime = 60;
//                monsterid = 9400613;
//                msg = "Valefor has appeared.";
//                pos1 = new Point(85, 66);
//                pos2 = new Point(85, 66);
//                pos3 = new Point(85, 66);
//                break;
//            case 931000500:
//                mobtime = 30 * 3600; //30 hours
//                monsterid = 9304005;
//                msg = "Jaira has appeared.";
//                pos1 = new Point(-872, -332);
//                pos2 = new Point(409, -572);
//                pos3 = new Point(-131, 0);
//                shouldSpawn = false;
//                break;
            case 104000400: // Mano
                mobtime = 2700;
                monsterid = 2220000;
                msg = "紅寶王出現了！";
                pos1 = new Point(439, 185);
                pos2 = new Point(301, -85);
                pos3 = new Point(107, -355);
                break;
            case 101030404: // Stumpy
                mobtime = 2700;
                monsterid = 3220000;
                msg = "樹妖王出現了！";
                pos1 = new Point(867, 1282);
                pos2 = new Point(810, 1570);
                pos3 = new Point(838, 2197);
                break;
            case 110040000: // King Clang
                mobtime = 1200;
                monsterid = 5220001;
                msg = "巨居蟹出現了！";
                pos1 = new Point(-355, 179);
                pos2 = new Point(-1283, -113);
                pos3 = new Point(-571, -593);
                break;
            case 250010304: // Tae Roon
                mobtime = 2100;
                monsterid = 7220000;
                msg = "流浪熊出現了！";
                pos1 = new Point(-210, 33);
                pos2 = new Point(-234, 393);
                pos3 = new Point(-654, 33);
                break;
            case 200010300: // Eliza
                mobtime = 1200;
                monsterid = 8220000;
                msg = "艾莉傑出現了！";
                pos1 = new Point(665, 83);
                pos2 = new Point(672, -217);
                pos3 = new Point(-123, -217);
                break;
            case 250010503: // Ghost Priest
                mobtime = 1800;
                monsterid = 7220002;
                msg = "喵仙怪人出現了！";
                pos1 = new Point(-303, 543);
                pos2 = new Point(227, 543);
                pos3 = new Point(719, 543);
                break;
            case 222010310: // Old Fox
                mobtime = 2700;
                monsterid = 7220001;
                msg = "九尾妖狐出現了！";
                pos1 = new Point(-169, -147);
                pos2 = new Point(-517, 93);
                pos3 = new Point(247, 93);
                break;
            case 107000300: // Dale
                mobtime = 1800;
                monsterid = 6220000;
                msg = "沼澤巨鱷出現了！";
                pos1 = new Point(710, 118);
                pos2 = new Point(95, 119);
                pos3 = new Point(-535, 120);
                break;
            case 100040105: // Faust
                mobtime = 1800;
                monsterid = 5220002;
                msg = "殭屍猴出現了！";
                pos1 = new Point(1000, 278);
                pos2 = new Point(557, 278);
                pos3 = new Point(95, 278);
                break;
            case 100040106: // Faust
                mobtime = 1800;
                monsterid = 5220002;
                msg = "藍色的霧氣變得更暗時出現了殭屍猴王";
                pos1 = new Point(1000, 278);
                pos2 = new Point(557, 278);
                pos3 = new Point(95, 278);
                break;
            case 220050100: // Timer
                mobtime = 1500;
                monsterid = 5220003;
                msg = "滴答滴答....咕咕鐘出現了!";
                pos1 = new Point(-467, 1032);
                pos2 = new Point(532, 1032);
                pos3 = new Point(-47, 1032);
                break;
            case 221040301: // Jeno
                mobtime = 2400;
                monsterid = 6220001;
                msg = "葛雷金剛出現了！";
                pos1 = new Point(-4134, 416);
                pos2 = new Point(-4283, 776);
                pos3 = new Point(-3292, 776);
                break;
            case 240040401: // Lev
                mobtime = 7200;
                monsterid = 8220003;
                msg = "寒霜冰龍出現了！";
                pos1 = new Point(-15, 2481);
                pos2 = new Point(127, 1634);
                pos3 = new Point(159, 1142);
                break;
            case 260010201: // Dewu
                mobtime = 3600;
                monsterid = 3220001;
                msg = "仙人長老出現了！";
                pos1 = new Point(-215, 275);
                pos2 = new Point(298, 275);
                pos3 = new Point(592, 275);
                break;
            case 261030000: // Chimera
                mobtime = 2700;
                monsterid = 8220002;
                msg = "奇美拉出現了！";
                pos1 = new Point(-1094, -405);
                pos2 = new Point(-772, -116);
                pos3 = new Point(-108, 181);
                break;
            case 230020100: // Sherp
                mobtime = 2700;
                monsterid = 4220000;
                msg = "火蚌殼出現了！";
                pos1 = new Point(-291, -20);
                pos2 = new Point(-272, -500);
                pos3 = new Point(-462, 640);
                break;
        }
        if (monsterid > 0) {
            map.addAreaMonsterSpawn(MapleLifeFactory.getMonster(monsterid), pos1, pos2, pos3, mobtime, msg, shouldSpawn);
        }
    }

    private void loadPortals(MapleMap map, MapleData port) {
        if (port == null) {
            return;
        }
        int nextDoorPortal = 0x80;
        for (MapleData portal : port.getChildren()) {
            MaplePortal myPortal = new MaplePortal(MapleDataTool.getInt(portal.getChildByPath("pt")));
            myPortal.setName(MapleDataTool.getString(portal.getChildByPath("pn")));
            myPortal.setTarget(MapleDataTool.getString(portal.getChildByPath("tn")));
            myPortal.setTargetMapId(MapleDataTool.getInt(portal.getChildByPath("tm")));
            myPortal.setPosition(new Point(MapleDataTool.getInt(portal.getChildByPath("x")), MapleDataTool.getInt(portal.getChildByPath("y"))));
            String script = MapleDataTool.getString("script", portal, null);
            if (script != null && script.equals("")) {
                script = null;
            }
            myPortal.setScriptName(script);

            if (myPortal.getType() == MaplePortal.DOOR_PORTAL) {
                myPortal.setId(nextDoorPortal);
                nextDoorPortal++;
            } else {
                myPortal.setId(Integer.parseInt(portal.getName()));
            }
            map.addPortal(myPortal);
        }
    }

    private MapleNodes loadNodes(final int mapid, final MapleData mapData) {
        MapleNodes nodeInfo = new MapleNodes(mapid);
        if (mapData.getChildByPath("nodeInfo") != null) {
            for (MapleData node : mapData.getChildByPath("nodeInfo")) {
                try {
                    if (node.getName().equals("start")) {
                        nodeInfo.setNodeStart(MapleDataTool.getInt(node, 0));
                        continue;
                    }
                    List<Integer> edges = new ArrayList<>();
                    if (node.getChildByPath("edge") != null) {
                        for (MapleData edge : node.getChildByPath("edge")) {
                            edges.add(MapleDataTool.getInt(edge, -1));
                        }
                    }
                    final MapleNodeInfo mni = new MapleNodeInfo(
                            Integer.parseInt(node.getName()),
                            MapleDataTool.getIntConvert("key", node, 0),
                            MapleDataTool.getIntConvert("x", node, 0),
                            MapleDataTool.getIntConvert("y", node, 0),
                            MapleDataTool.getIntConvert("attr", node, 0), edges);
                    nodeInfo.addNode(mni);
                } catch (NumberFormatException e) {
                } //start, end, edgeInfo = we dont need it
            }
            nodeInfo.sortNodes();
        }
        for (int i = 1; i <= 7; i++) {
            if (mapData.getChildByPath(String.valueOf(i)) != null && mapData.getChildByPath(i + "/obj") != null) {
                for (MapleData node : mapData.getChildByPath(i + "/obj")) {
                    if (node.getChildByPath("SN_count") != null && node.getChildByPath("speed") != null) {
                        int sn_count = MapleDataTool.getIntConvert("SN_count", node, 0);
                        String name = MapleDataTool.getString("name", node, "");
                        int speed = MapleDataTool.getIntConvert("speed", node, 0);
                        if (sn_count <= 0 || speed <= 0 || name.equals("")) {
                            continue;
                        }
                        final List<Integer> SN = new ArrayList<>();
                        for (int x = 0; x < sn_count; x++) {
                            SN.add(MapleDataTool.getIntConvert("SN" + x, node, 0));
                        }
                        final MaplePlatform mni = new MaplePlatform(
                                name, MapleDataTool.getIntConvert("start", node, 2), speed,
                                MapleDataTool.getIntConvert("x1", node, 0),
                                MapleDataTool.getIntConvert("y1", node, 0),
                                MapleDataTool.getIntConvert("x2", node, 0),
                                MapleDataTool.getIntConvert("y2", node, 0),
                                MapleDataTool.getIntConvert("r", node, 0), SN);
                        nodeInfo.addPlatform(mni);
                    } else if (node.getChildByPath("tags") != null) {
                        String name = MapleDataTool.getString("tags", node, "");
                        nodeInfo.addFlag(new Pair<>(name, name.endsWith("3") ? 1 : 0)); //idk, no indication in wz
                    }
                }
            }
        }
        // load areas (EG PQ platforms)
        if (mapData.getChildByPath("area") != null) {
            int x1, y1, x2, y2;
            Rectangle mapArea;
            for (MapleData area : mapData.getChildByPath("area")) {
                x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
                y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
                x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
                y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
                mapArea = new Rectangle(x1, y1, (x2 - x1), (y2 - y1));
                nodeInfo.addMapleArea(mapArea);
            }
        }
        if (mapData.getChildByPath("CaptureTheFlag") != null) {
            final MapleData mc = mapData.getChildByPath("CaptureTheFlag");
            for (MapleData area : mc) {
                nodeInfo.addGuardianSpawn(new Point(MapleDataTool.getInt(area.getChildByPath("FlagPositionX")), MapleDataTool.getInt(area.getChildByPath("FlagPositionY"))), area.getName().startsWith("Red") ? 0 : 1);
            }
        }
        if (mapData.getChildByPath("directionInfo") != null) {
            final MapleData mc = mapData.getChildByPath("directionInfo");
            for (MapleData area : mc) {
                DirectionInfo di = new DirectionInfo(Integer.parseInt(area.getName()), MapleDataTool.getInt("x", area, 0), MapleDataTool.getInt("y", area, 0), MapleDataTool.getInt("forcedInput", area, 0) > 0);
                final MapleData mc2 = area.getChildByPath("eventQ");
                if (mc2 != null) {
                    for (MapleData event : mc2) {
                        di.eventQ.add(MapleDataTool.getString(event));
                    }
                }
                nodeInfo.addDirection(Integer.parseInt(area.getName()), di);
            }
        }
        if (mapData.getChildByPath("monsterCarnival") != null) {
            final MapleData mc = mapData.getChildByPath("monsterCarnival");
            if (mc.getChildByPath("mobGenPos") != null) {
                for (MapleData area : mc.getChildByPath("mobGenPos")) {
                    nodeInfo.addMonsterPoint(MapleDataTool.getInt(area.getChildByPath("x")),
                            MapleDataTool.getInt(area.getChildByPath("y")),
                            MapleDataTool.getInt(area.getChildByPath("fh")),
                            MapleDataTool.getInt(area.getChildByPath("cy")),
                            MapleDataTool.getInt("team", area, -1));
                }
            }
            if (mc.getChildByPath("mob") != null) {
                for (MapleData area : mc.getChildByPath("mob")) {
                    nodeInfo.addMobSpawn(MapleDataTool.getInt(area.getChildByPath("id")), MapleDataTool.getInt(area.getChildByPath("spendCP")));
                }
            }
            if (mc.getChildByPath("guardianGenPos") != null) {
                for (MapleData area : mc.getChildByPath("guardianGenPos")) {
                    nodeInfo.addGuardianSpawn(new Point(MapleDataTool.getInt(area.getChildByPath("x")), MapleDataTool.getInt(area.getChildByPath("y"))), MapleDataTool.getInt("team", area, -1));
                }
            }
            if (mc.getChildByPath("skill") != null) {
                for (MapleData area : mc.getChildByPath("skill")) {
                    nodeInfo.addSkillId(MapleDataTool.getInt(area));
                }
            }
        }
        return nodeInfo;
    }

    public boolean destroyMap(int mapid) {
        return destroyMap(mapid, false);
    }

    public boolean destroyMap(int mapid, boolean Remove) {
        synchronized (this.maps) {
            if (this.maps.containsKey(mapid)) {
                if (Remove) {
                    this.DeStorymaps.put(mapid, 0);
                    this.maps.remove(mapid);
                }
                return this.maps.remove(mapid) != null;

            }
        }
        return false;
    }

    public void HealMap(int mapid) {
        synchronized (this.maps) {
            if (this.DeStorymaps.containsKey(mapid)) {
                this.DeStorymaps.remove(mapid);
            }
        }
    }
}
